#include<cstring>
#include<map>
#include<thread>
#include<utility>
#include"DealConn.h"
#include"DealRet.h"
#include"Log.h"
#include"DealReq.h"

using time_point = std::chrono::system_clock::time_point;

std::chrono::duration<int, std::ratio<10>> DealConn::maxLiveTime;
std::map<int, time_point> DealConn::lastActTimes;
ThreadPool<int> DealConn::reqs(DealReq::dealReq);
ThreadPool<int> DealConn::rets(DealRet::dealRet);
Timer DealConn::timer;
std::atomic_flag DealConn::lock = ATOMIC_FLAG_INIT;
int DealConn::epollfd;

void DealConn::closeConn(const int &fd){
  while(lock.test_and_set()) std::this_thread::yield();
  if(lastActTimes[fd] < std::chrono::system_clock::now()){
    lastActTimes.erase(fd);
    lock.clear();
    freefd(fd);
    DealReq::eraseReq(fd);
    DealRet::eraseRet(fd);
  }
  lock.clear();
}

void DealConn::freefd(int fd){
  epoll_event event;
  event.data.fd = fd;
  event.events = EPOLLOUT | EPOLLIN | EPOLLET | EPOLLRDHUP;
  epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &event);
  close(fd);
}

void DealConn::addfd(int fd){
  epoll_event event;
  event.data.fd = fd;
  event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
  epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
  int old_option = fcntl(fd, F_GETFL);
  int new_option = old_option | O_NONBLOCK;
  fcntl(fd, F_SETFL, new_option);
}

void DealConn::setTime(int fd, time_point &&t){
  timer.addTask(fd, t);
  while(lock.test_and_set()) std::this_thread::yield();
  lastActTimes[fd] = t;
  lock.clear();
}

void DealConn::resetfd(int fd){
  epoll_event event;
  event.data.fd = fd;
  event.events = EPOLLOUT | EPOLLIN | EPOLLET | EPOLLRDHUP;
  epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);
}

void DealConn::et(epoll_event* events, int number, int listenfd){
  for(int i = 0; i < number; i++){
    int sockfd = events[i].data.fd;
    if(sockfd == listenfd){
      struct sockaddr_in client_address;
      socklen_t client_addrlength = sizeof(client_address);
      int connfd = accept(listenfd, (struct sockaddr*)&client_address,
			  &client_addrlength);
      addfd(connfd);
      setTime(connfd, std::chrono::system_clock::now() + maxLiveTime);
    }else{
      if(events[i].events & EPOLLRDHUP){
	setTime(sockfd, std::chrono::system_clock::now());
      }else if(events[i].events & EPOLLIN){
	reqs.addTask(sockfd);
	setTime(sockfd, std::chrono::system_clock::now() + maxLiveTime);
      }else if(events[i].events & EPOLLOUT){
	rets.addTask(sockfd);
      }else{
	std::cout<< "其他事件\n" << std::endl;
      }
    }
  }
}

void DealConn::start(){
  Log::lock_log.clear();
  DealReq::lock_sts.clear();
  DealRet::lock_sts.clear();
  lock.clear();
  
  int ret = 0;
  struct sockaddr_in address;
  bzero(&address, sizeof(address));
  address.sin_family = PF_INET;
  address.sin_addr.s_addr = htonl(INADDR_ANY);
  address.sin_port = htons(port);
  int listenfd = socket(AF_INET, SOCK_STREAM, 0);
  assert(listenfd >= 0);
  ret = bind(listenfd, (struct sockaddr *)&address, sizeof(address));
  assert(ret != -1);
  ret = listen(listenfd, 5);
  assert(ret != -1);
  epoll_event events[esize];
  epollfd = epoll_create(esize);
  assert(epollfd != -1);
  addfd(listenfd);
  while(true){
    ret = epoll_wait(epollfd, events, esize, -1);
    if(ret < 0){
      std::cout<<"epoll failtrue\n"<<std::endl;
      close(listenfd);
      break;
    }
    et(events, ret, listenfd);
  }
}
      
